﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel.Composition;
using Psm.Container;
using Psm.Logging;
using Psm.Transport;

namespace Psm.Transport.TcpTransport
{
    [PartCreationPolicy(CreationPolicy.NonShared)]
    [Export("TCP_TRANSPORT", typeof(IServerTransport))]
    class TcpServer : IServerTransport
    {
        private static ILogger logger = Repository.GetInstance<ILogger>();

        private Socket listener;
        private TcpHostConfiguration configuration;
        public event EventHandler<NewClientEventArgs> NewClient;

        public void Start()
        {
            IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(configuration.Ip), int.Parse(configuration.Port));
            listener = new Socket(localEP.Address.AddressFamily,SocketType.Stream, ProtocolType.Tcp);
            try
            {
                listener.Bind(localEP);
                listener.Listen(100);
                logger.Info("Listening at {0}:{1}", configuration.Ip, configuration.Port);
                listener.BeginAccept(new AsyncCallback(this.AcceptCallback), null);
            }
            catch (Exception ex)
            {
                logger.Exception(ex);
            }
        }

        private void AcceptCallback(IAsyncResult ar)
        {
            try
            {       
                Socket clientSocket = listener.EndAccept(ar);
                TcpClient clientTransport = new TcpClient(clientSocket);
                OnNewClient(clientTransport);
                this.listener.BeginAccept(new AsyncCallback(this.AcceptCallback), null);
            }
            catch (ObjectDisposedException )
            {
                //workaround for socked dispose exception if socket is already closed, need to look into the right way
            }
            catch (Exception ex)
            {
                logger.Exception(ex);
            }
        }

        private void OnNewClient(TcpClient clientTransport)
        {
            if (this.NewClient != null) //TODO: thread safety?
                NewClient(this, new NewClientEventArgs(clientTransport));
        }

        public void Stop()
        {
            try
            {
                logger.Info("Closing listener on {0}:{1}", configuration.Ip, configuration.Port);
                if (this.listener != null)
                    this.listener.Close();
            }
            catch (Exception ex)
            {
                logger.Exception(ex);
            }
        }

        

        public void Configure(string configuration)
        {
            this.configuration = TcpHostsConfiguration.GetConfiguration(configuration);
        }
    }
}
